#include "demuxer.h"

#define ERRMSG(errnum,msg) \
   char errBuf[1024]; \
   av_strerror(res,errBuf,sizeof(errBuf));  \
   qDebug() << #msg << errBuf ;

//#define GOTOEND(codition)


Demuxer::Demuxer()
{

}

int Demuxer::demuxer(const char *videoPath, YUVFileSpec &yuvFileSpec, PCMFileSpec &pcmFileSpec)
{

    int res = 0;
    _pcmFileSpec = &pcmFileSpec;
    _yuvFileSpec = &yuvFileSpec;



    res = avformat_open_input(&_fmtContext,videoPath,nullptr,nullptr);

    if(res < 0) {

        ERRMSG(res,#avformat_open_input);
        goto end;
    }

    res = avformat_find_stream_info(_fmtContext,nullptr);

    if(res < 0) {

        ERRMSG(res,#avformat_find_stream_info);
        goto end;
    }

    av_dump_format(_fmtContext,0,videoPath,0);
    fflush(stderr);


    res = initVideo();

    if(res < 0) {

        qDebug() << "initVideo error";
        goto end;
    }

    res = initAudio();

    if(res < 0) {

        qDebug() << "initAudio error";
        goto end;
    }

    _packet = av_packet_alloc();

    if(_packet == nullptr) {

        qDebug() << "av_packet_alloc fail";
        goto end;
    }

    _packet->size = 0;
    _packet->data = nullptr;

    _frame = av_frame_alloc();

    if(_frame == nullptr){

        qDebug() << "av_frame_alloc fail";
        goto end;
    }

    while(av_read_frame(_fmtContext,_packet) == 0) {

        if(_packet->stream_index == _videoIndex) {

            decode(_videoCodecCxt,_packet,&Demuxer::decodeVideo);

        }else if (_packet->stream_index == _audioIndex) {

            decode(_audioCodecCxt,_packet,&Demuxer::decodeAudio);
        }

        av_packet_unref(_packet);
    }


    // 刷缓存
    decode(_videoCodecCxt,nullptr,&Demuxer::decodeVideo);
    decode(_audioCodecCxt,nullptr,&Demuxer::decodeAudio);

end:

    if(_frame != nullptr) {

        av_frame_free(&_frame);
    }

    if(_packet != nullptr) {

        av_packet_free(&_packet);
    }

    if(_videoOutFile != nullptr) {

        _videoOutFile->close();
        _videoOutFile = nullptr;
    }

    if(_audioOutFile != nullptr) {

        _audioOutFile->close();
        _audioOutFile = nullptr;
    }

    if(_videoCodecCxt != nullptr) {

       avcodec_free_context(&_videoCodecCxt);
    }

    if(_audioCodecCxt != nullptr) {

        avcodec_free_context(&_audioCodecCxt);
    }

    av_freep(&_imageBuffer[0]);

    avformat_close_input(&_fmtContext);


    return res;
}

int Demuxer::init(AVMediaType mediaType, int *mediaIndex, AVCodecContext **codecCxt)
{

    int res = av_find_best_stream(_fmtContext,mediaType,-1,-1,nullptr,0);

    if(res < 0) {

       ERRMSG(res,#av_find_best_stream);
       return res;
    }

    *mediaIndex = res;
    AVStream* stream = _fmtContext->streams[*mediaIndex];

    if(stream == nullptr) {

       return -1;
    }

    AVCodec* codec = avcodec_find_decoder(stream->codecpar->codec_id);

    if(codec == nullptr) {

      qDebug() << "avcodec_find_decoder fail";
      return -1;
    }

    *codecCxt = avcodec_alloc_context3(codec);

    if(*codecCxt == nullptr) {

        qDebug() << "avcodec_alloc_context3 fail";
        return -1;
    }


   res = avcodec_parameters_to_context(*codecCxt,stream->codecpar);

   if(res < 0) {

       qDebug() << "avcodec_parameters_to_context fail";
       return -1;
   }

   res = avcodec_open2(*codecCxt,codec,nullptr);

   if(res < 0) {

       qDebug() << "avcodec_open2 fail";
       return -1;
   }
   return 0;
}

int Demuxer::initVideo()
{

    int res = init(AVMEDIA_TYPE_VIDEO,&_videoIndex,&_videoCodecCxt);

    if(res != 0) {

        qDebug() << "init fail";
        return -1;
    }


    _yuvFileSpec->setWidth(_videoCodecCxt->width);
    _yuvFileSpec->setHeight(_videoCodecCxt->height);
    _yuvFileSpec->setFormat(_videoCodecCxt->pix_fmt);

   AVRational rational = av_guess_frame_rate(_fmtContext,_fmtContext->streams[_videoIndex],nullptr);

   _yuvFileSpec->setFPS(rational.num/rational.den);
   _yuvFileSpec->calImageSize();

   _videoOutFile = new QFile(_yuvFileSpec->getFilePath());

   if(!_videoOutFile->open(QFile::WriteOnly)) {

       qDebug() << _yuvFileSpec->getFilePath() << "open fail";
       return -1;
   }

   res = av_image_alloc(_imageBuffer,_lineSizes,_videoCodecCxt->width,_videoCodecCxt->height,_videoCodecCxt->pix_fmt,1);

   if(res <0) {

       ERRMSG(res,av_image_alloc);
   }

//   qDebug() << "res:" << res;

   return 0;
}

int Demuxer::initAudio()
{
    int res = init(AVMEDIA_TYPE_AUDIO,&_audioIndex,&_audioCodecCxt);

    if(res != 0) {

        qDebug() << "init fail";
        return -1;
    }

    _pcmFileSpec->setSampleRate(_audioCodecCxt->sample_rate);
    _pcmFileSpec->setChannelLayout(_audioCodecCxt->channel_layout);
    _pcmFileSpec->setSampleFormat(_audioCodecCxt->sample_fmt);

    _pcmFileSpec->calFrameSize();
    _audioOutFile = new QFile(_pcmFileSpec->getFilePath());

    if(!_audioOutFile->open(QFile::WriteOnly)) {

        qDebug() << _pcmFileSpec->getFilePath() << "open fail";
        return -1;
    }

    return 0;
}

int Demuxer::decode(AVCodecContext *_codecCxt, AVPacket* packet, void (Demuxer::*func)(void))
{

    int res = avcodec_send_packet(_codecCxt,packet);

    if(res == AVERROR(EAGAIN) || res == AVERROR_EOF ) {

        return 0;
    }else if(res < 0) {

        qDebug() << "avcodec_send_packet error";
        return res;
    }


    while (true) {

        res = avcodec_receive_frame(_codecCxt,_frame);

        if(res == AVERROR(EAGAIN) || res == AVERROR_EOF) {

            return 0;
        }else if(res < 0) {

            qDebug() << "avcodec_receive_frame error";
            return res;
        }

        (this->*func)();

        av_frame_unref(_frame);
    }

    return 0;
}

void Demuxer::decodeVideo()
{


    av_image_copy(_imageBuffer,
                  _lineSizes,
                  (const uint8_t **)(_frame->data),
                  _frame->linesize,
                  _yuvFileSpec->getFormat(),
                  _yuvFileSpec->getWidth(),
                  _yuvFileSpec->getHeight());

    qDebug() << "imageSize " << _yuvFileSpec->getImageSize();
    _videoOutFile->write((char*)_imageBuffer[0],_yuvFileSpec->getImageSize());

}

void Demuxer::decodeAudio()
{

    if(av_sample_fmt_is_planar(_pcmFileSpec->getSampleFormat())){

        //planner，则 左右声道分开 llllllrrrrrr,硬件不能播放planner格式的音频，
        //必须转换成非planner格式，才能被硬件播放

        // sflp -> s32le
        // 每一个声道采集的数量是一样的
        int sampleAllFrameNumber = _frame->nb_samples;
        int numChannels = _audioCodecCxt->channels;
       // qDebug() << "sampleAllFrameNumber:" << sampleAllFrameNumber << "numChannels" << numChannels << "PerChannelSize" << _pcmFileSpec->getPerChannelSize();

        for(int i = 0; i < sampleAllFrameNumber;i++) {

            for(int j = 0; j < numChannels;j++) {

                _audioOutFile->write((char*)(_frame->data[j] + i*_pcmFileSpec->getPerChannelSize()),_pcmFileSpec->getPerChannelSize());
            }
        }

    }else {

        // 非planner，则 左右声道 一起排列 lrlr
        qDebug() << "采集的音频数量 nb_samples:" << _frame->nb_samples;
        _audioOutFile->write((char*)_frame->data,_frame->nb_samples * _pcmFileSpec->getFrameSize());
    }
}




